package info.niwota.web.proxy;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jvs.peer.util.Log;
import x.org.apache.http.HttpResponseInterceptor;
import x.org.apache.http.impl.DefaultConnectionReuseStrategy;
import x.org.apache.http.impl.DefaultHttpResponseFactory;
import x.org.apache.http.params.CoreConnectionPNames;
import x.org.apache.http.params.CoreProtocolPNames;
import x.org.apache.http.params.HttpParams;
import x.org.apache.http.params.SyncBasicHttpParams;
import x.org.apache.http.protocol.HttpProcessor;
import x.org.apache.http.protocol.HttpRequestHandler;
import x.org.apache.http.protocol.HttpRequestHandlerRegistry;
import x.org.apache.http.protocol.HttpService;
import x.org.apache.http.protocol.ImmutableHttpProcessor;
import x.org.apache.http.protocol.ResponseConnControl;
import x.org.apache.http.protocol.ResponseContent;
import x.org.apache.http.protocol.ResponseDate;
import x.org.apache.http.protocol.ResponseServer;

public class RequestListenerThread extends Thread {
	private static final String TAG = "RequestListenerThread";

	private int timeout = 60000; // 5000;

	private ExecutorService pool;
	
	private final ServerSocket serverSocket;
	private final HttpParams params;
	private final HttpService httpService;

	private HttpRequestHandlerRegistry registry;

	public RequestListenerThread(int port) throws Exception {
		this(new ServerSocket(port));
	}

	public RequestListenerThread(ServerSocket serverSocket)
			throws Exception {
		
		this.serverSocket = serverSocket;
		this.params = new SyncBasicHttpParams();
		this.params
				.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout)
				.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
						8 * 1024)
				.setBooleanParameter(
						CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
				.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
				.setParameter(CoreProtocolPNames.ORIGIN_SERVER,
						"HttpComponents/1.1");

		// Set up the HTTP protocol processor
		HttpProcessor httpproc = new ImmutableHttpProcessor(
				new HttpResponseInterceptor[] { new ResponseDate(),
						new ResponseServer(), new ResponseContent(),
						new ResponseConnControl() });

		// Set up request handlers
		this.registry = new HttpRequestHandlerRegistry();

		// Set up the HTTP service
		this.httpService = new HttpService(httpproc,
				new DefaultConnectionReuseStrategy(),
				new DefaultHttpResponseFactory(), this.registry, this.params);
	}

	public void run() {
		if (Log.INFO) {
			Log.i(TAG, "Listening on port "
				+ this.serverSocket.getLocalPort());
		}
		
		pool = Executors.newCachedThreadPool();
		
		while (!Thread.interrupted()) {
			ServerSocket ss = this.serverSocket;
			try {

				Socket s = ss.accept();

				if (Log.DEBUG) {
					Log.d(TAG, "accepted: " + ss.getInetAddress()
						+ " incoming connection from " + s.getInetAddress());
				}
				//
				Runnable t = new HttpWorkerThread(this.httpService, this.params,
						s);
				pool.execute(t);
				
			} catch (InterruptedIOException ex) {
				break;
			} catch (IOException e) {
				if (Log.DEBUG) {
					Log.d(TAG, "I/O error initialising connection thread: "
						+ e.getMessage());
				}
				break;
			}
		}
		
		pool.shutdownNow();
	}

	public void register(String pattern, HttpRequestHandler handler) {
		registry.register(pattern, handler);
	}

}
